package com.lagou.edu.mvcframework.servlet;

import com.lagou.edu.mvcframework.annotations.*;
import com.lagou.edu.mvcframework.pojo.Handle;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.method.HandlerMethod;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * @author Mrwg
 * @date 2020/4/12
 * @description
 */
public class LgDispatcherServlet extends HttpServlet {
    private Properties properties = new Properties();
    private List<String> calssNames = new ArrayList<>(); //缓存扫描到类的全限定类名

    private Map<String, Object> ioc = new HashMap<>();

    //    private Map<String, Method> handleMapping = new HashMap<>();
    private List<Handle> handleMapping = new ArrayList<>();

    /**
     * 1、加载配置文件，
     * 2、扫描注解
     * 3、初始化bean对象（实现IOC基于注解）
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {

        //1、加载配置文件，
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        //2、扫描注解
        doScan(properties.getProperty("scanPankage"));
        //3、初始化bean对象（实现IOC基于注解）
        doInstance();
        //4、实现依赖注入
        doAutowired();
//       5、 构造一个HandlerMapping 处理器映射器
        initHandlerMapping();

//        6、等待请求进入，处理请求
        System.out.println("mvc 初始化完成");
    }

    /**
     * 构造一个HandlerMapping 处理器映射器
     */
    private void initHandlerMapping() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();
            if (!aClass.isAnnotationPresent(LagouController.class)) {
                continue;
            }
            String baseUrl = "";
            if (aClass.isAnnotationPresent(LagouRequestMapping.class)) {
                LagouRequestMapping annotation = aClass.getAnnotation(LagouRequestMapping.class);
                baseUrl = annotation.value();   // 等同于 /demo
            }
            //获取方法
            Method[] methods = aClass.getMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                if (!method.isAnnotationPresent(LagouRequestMapping.class)) {
                    continue;
                }
                //判断时候包含admin
                LagouSecurity security = method.getAnnotation(LagouSecurity.class);
                if (Arrays.asList(security.value()).contains("admin")) {
                    //有标识
                    LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class);
                    String methodUrl = annotation.value();
                    String url = baseUrl + methodUrl;
                    //把method所有信息和和url封装为一个 Handle
                    Handle handle = new Handle(entry.getValue(), method, Pattern.compile(url));
                    //计算参数位置信息 query(HttpServletRequest request, HttpResponse response, String name)
                    Parameter[] parameters = method.getParameters();
                    for (int j = 0; j < parameters.length; j++) {
                        Parameter parameter = parameters[j];
                        if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
                            handle.getParamIdexMapping().put(parameter.getType().getSimpleName(), j);
                        } else {
                            handle.getParamIdexMapping().put(parameter.getName(), j);
                        }

                    }
                    handleMapping.add(handle);
                }
            }
//            } else {
//            }
        }
    }

    /**
     * 实现依赖注入
     */
    private void doAutowired() {
        if (ioc.isEmpty()) {
            return;
        }
        //有对象赖注入
        for (Map.Entry<String, Object> entryntry : ioc.entrySet()) {
            //获取bean对象中的字段信息
            Field[] declaredFields = entryntry.getValue().getClass().getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                if (!declaredField.isAnnotationPresent(LagouAutowired.class)) {
                    continue;
                }
                //有该注解
                LagouAutowired annotation = declaredField.getAnnotation(LagouAutowired.class);
                String beanName = annotation.value();
                if ("".equals(beanName.trim())) {
                    beanName = declaredField.getType().getName();
                }
                //开启赋值
                declaredField.setAccessible(true);
                try {
                    declaredField.set(entryntry.getValue(), ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * IOC 容器
     * 基于calss
     */
    private void doInstance() {
        if (calssNames.size() == 0) {
            return;
        }
        try {
            for (int i = 0; i < calssNames.size(); i++) {
                String calssName = calssNames.get(i); //com.lagou.demo.controller.DemoController
                Class<?> aClass = Class.forName(calssName);
                //区分controller \service
                if (aClass.isAnnotationPresent(LagouController.class)) {
                    String simpleName = aClass.getSimpleName();
                    String lowerSimpleName = lowerFirst(simpleName);
                    Object o = aClass.newInstance();
                    ioc.put(lowerSimpleName, o);
                } else if (aClass.isAnnotationPresent(LagouService.class)) {
                    LagouService annotation = aClass.getAnnotation(LagouService.class);
                    //获取注解value
                    String beanName = annotation.value();
                    //如果指定了id，就已指定的为准
                    if (!"".equals(beanName.trim())) {
                        ioc.put(beanName, aClass.newInstance());
                    } else {
                        beanName = lowerFirst(aClass.getName());
                        ioc.put(beanName, aClass.newInstance());
                    }
                    //service
                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (int j = 0; j < interfaces.length; j++) {
                        Class<?> anInterface = interfaces[j];
                        ioc.put(anInterface.getName(), aClass.newInstance());
                    }
                } else {
                    continue;
                }

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 首字母大小写转换
     *
     * @return
     */
    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if ('A' < chars[0] && chars[0] < 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    /**
     * 扫描包文件
     * scanPankage=com.lagou.demo
     */
    private void doScan(String scanPankage) {
        String scanPankagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPankage.replaceAll("\\.", "/");
        File file = new File(scanPankagePath);
        File[] files = file.listFiles();
        for (File file1 : files) {
            if (file1.isDirectory()) {//存在只文件夹
                doScan(scanPankage + "." + file1.getName());
            } else if (file1.getName().endsWith(".class")) {
                String calssNmae = scanPankage + "." + file1.getName().replaceAll(".class", "");
                calssNames.add(calssNmae);
            }
        }

    }

    /*加載配置文件*/
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //接收请求，获取url
//        String requestURI = req.getRequestURI();
//        Method method = handleMapping.get(requestURI);
        //反射调用，需要传入对象，需要传入参数，此处无法完成调用，没有吧对象缓存起来，也没有参数！！！！！改造initHandlerMapping()
//        method.invoke();
        Handle handle = getHandle(req);
        if (handle == null) {
            resp.getWriter().write("404 not found");
            return;
        }
        //参数绑定
        //获取所有参数类型数组
        Class<?>[] parameterTypes = handle.getMethod().getParameterTypes();
        //创建一个新的数组
        Object[] paraValus = new Object[parameterTypes.length];
        Map<String, String[]> parameterMap = req.getParameterMap();
        //遍历request 中所有的参数
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            String value = StringUtils.join(param.getValue(), ",");
            if (handle.getParamIdexMapping().containsKey(param.getKey())) {
                Integer index = handle.getParamIdexMapping().get(param.getKey());
                paraValus[index] = value;
            }
        }
        Integer requestIndex = handle.getParamIdexMapping().get(HttpServletRequest.class.getSimpleName());
        paraValus[requestIndex] = req;

        Integer responseIndex = handle.getParamIdexMapping().get(HttpServletResponse.class.getSimpleName());
        paraValus[responseIndex] = resp;
        try {
            Object invoke = handle.getMethod().invoke(handle.getController(), paraValus);
            PrintWriter pw = resp.getWriter();
            if (invoke!=null) {
                pw.println("name:" + invoke);
            }else{
                pw.println("无访问权限！");
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Handle getHandle(HttpServletRequest req) {
        if (handleMapping.isEmpty()) {
            return null;
        }
        String requestURL = req.getRequestURI();
        for (Handle handle : handleMapping) {
            Matcher matcher = handle.getPattern().matcher(requestURL);
            if (!matcher.matches()) {
                continue;
            }
            return handle;
        }
        return null;
    }
}
